import datetime

from django.db import models
from django import oldforms
from django.utils.functional import curry

import audio.newforms

class DurationProxy(object):
    def __init__(self, field):
        self.field = field

    def __get__(self, obj, type=None):
        return obj.__dict__[self.field.attname]

    def __set__(self, obj, value):
        if not isinstance(value, datetime.timedelta):
            if value is None or value == '':
                value = None
            else:
                value = datetime.timedelta(seconds=float(value))
        obj.__dict__[self.field.attname] = value

class DurationField(models.Field):
    def __init__(self, *args, **kwargs):
        self.max_digits, self.decimal_places = 20, 6
        super(DurationField, self).__init__(*args, **kwargs)

    def get_internal_type(self):
        return "DecimalField"

    def contribute_to_class(self, cls, name):
        super(DurationField, self).contribute_to_class(cls, name)
        setattr(cls, name, DurationProxy(self))

    def get_db_prep_save(self, value):
        if value is not None:
            return str(value.days * 24 * 3600 + value.seconds + float(value.microseconds) / 1000000)

    def to_python(self, value):
        if isinstance(value, datetime.timedelta):
            return value
        try:
            return datetime.timedelta(seconds=float(value))
        except (TypeError, ValueError):
            raise validators.ValidationError('This value must be a real number.')
        except OverflowError:
            raise validators.ValidationError('The maximum allowed value is %s' % datetime.timedelta.max)

    def create(self, value):
        return datetime.timedelta(seconds=float(value) or 0)

    def flatten_data(self, follow, obj=None):
        val = self._get_val_from_obj(obj)
        if val is None or val is '':
            return ''
        return {self.attname: self.get_db_prep_save(val)}

    def formfield(self, form_class=audio.newforms.SplitDurationField, **kwargs):
        return super(DurationField, self).formfield(form_class, **kwargs)

    def get_manipulator_field_objs(self):
        return [curry(oldforms.DecimalField, max_digits=self.max_digits, decimal_places=self.decimal_places)]

class AudioField(models.FileField):
    def __init__(self, verbose_name=None, name=None, title_field=None, duration_field=None, bitrate_field=None, **kwargs):
        self.title_field, self.duration_field, self.bitrate_field = title_field, duration_field, bitrate_field
        models.FileField.__init__(self, verbose_name, name, **kwargs)

    def get_internal_type(self):
        return "FileField"

    def get_manipulator_field_objs(self):
        return [oldforms.FileUploadField, oldforms.HiddenField]

    def contribute_to_class(self, cls, name):
        super(AudioField, self).contribute_to_class(cls, name)
        # Add get_BLAH_title and get_BLAH_duration methods, but only if the
        # audio field doesn't have title and duration cache fields.
        if not self.title_field:
            setattr(cls, 'get_%s_title' % self.name, curry(_get_title, field=self))
        if not self.duration_field:
            setattr(cls, 'get_%s_duration' % self.name, curry(_get_duration, field=self))
        if not self.bitrate_field:
            setattr(cls, 'get_%s_bitrate' % self.name, curry(_get_bitrate, field=self))

def _get_audio_details(obj, field):
    cachename = "__%s_audio_details" % field.name
    if not hasattr(self, cachename):
        import mutagen
        filename = obj._get_FIELD_filename(field)
        file = mutagen.File(filename)
        details = {
            'duration': datetime.timedelta(seconds=file.info.length),
            'bitrate': file.info.bitrate,
        }
        try:
            details['title'] = unicode(file['title'])
        except KeyError:
            details['title'] = unicode(file['TIT2'])
        setattr(obj, cachename, details)
    return getattr(self, cachename)

def _get_title(obj, field):
    return _get_audio_details(obj, field)['title']

def _get_duration(obj, field):
    return _get_audio_details(obj, field)['duration']

def _get_bitrate(obj, field):
    return _get_audio_details(obj, field)['bitrate']

